/**
 * 
 */
package gpl.scotlandyard.services.basics;

import gpl.scotlandyard.beans.basics.Board;
import gpl.scotlandyard.beans.basics.Link;
import gpl.scotlandyard.beans.basics.Node;
import gpl.scotlandyard.beans.basics.Player;
import gpl.scotlandyard.exceptions.IllegalMoveException;

import org.apache.log4j.Logger;

/** @author norbert */
public class BoardManager {

  private static final Logger LOG = Logger.getLogger(BoardManager.class);

  private Board board = new Board();

  private PlayerManager playerManager;

  /** Creates BoardManager. */
  BoardManager() {
    playerManager = new PlayerManager(this);
    LOG.info("BoardManager created");
  }

  /** @return the board */
  public Board getBoard() {
    return board;
  }

  /**
   * @return playerManager
   */
  public PlayerManager getPlayerManager() {
    return playerManager;
  }

  /** Moves current player on this {@link Link}.
   * 
   * @param link
   * @throws IllegalMoveException */
  public void moveCurrentPlayer(Link link) throws IllegalMoveException {
    Player player = board.getCurrentPlayer();
    if (player == null) {
      throw new IllegalStateException("current player is null");
    }

    playerManager.move(player, link);
    if (!player.isFugitive()) {
      playerManager.credit(board.getFugitive(), link);
    }

    controlGameState();
    if (!board.isGameFinished()) {
      board.nextPlayer(player.isProceedDoubleMove());
    }
  }

  /**
   * 
   */
  public void startGame() {

    board.start();
  }

  /**
   * 
   */
  private void controlGameState() {
    Player player = board.getCurrentPlayer();
    if (player != null && player.isFugitive() && isFugitiveHold()) {
      board.setGameFinished(Board.FUGITIVE_HOLD);
    } else if (hasFugitiveWin()) {
      board.setGameFinished(Board.FUGITIVE_WIN);
    } else if (hasDetectivesWin()) {
      board.setGameFinished(Board.DETECTIVES_WIN);
    }
  }

  /** Returns true if a detective is on fugitive position.
   * 
   * @return true if a detective is on fugitive position otherwise false. */
  private boolean hasDetectivesWin() {
    Node fNode = board.getFugitive().getNode();
    for (Player detective : board.getDetectives()) {
      if (detective.getNode().equals(fNode)) {
        return true;
      }
    }
    return false;
  }

  /** Returns true if fugitive win. Tests if detectives don't win and if turn is over MAX_TURN.
   * 
   * @return true if fugitive win otherwise false. */
  private boolean hasFugitiveWin() {
    return !hasDetectivesWin() && !isFugitiveHold() && board.getCurrentTurn() > board.getMaxTurn();
  }

  /** @return */
  boolean isFugitiveHold() {
    return playerManager.getMoves(board.getFugitive()).size() == 0;
  }

  /**
   * @param playerManager
   */
  void setPlayerManager(PlayerManager playerManager) {
    this.playerManager = playerManager;
    this.playerManager.setBoardManager(this);
  }

}
